In the field of computer software, the term “process” generally refers to a coherent sequence of steps or instructions undertaken by a computer program or application. From the perspective of a user, a data processing system may support multiple processes concurrently. For example, the processes that are executing concurrently in a data processing system may include an Internet browser application and a word processing application.
In addition, an application may utilize a technique known as “multithreading” to run two or more parts of a particular process concurrently, or virtually concurrently. For instance, a web browser process may launch two separate threads to download data from two different sources concurrently. Each of those threads may include a separate copy of a certain subset of instructions from the overall application. For example, each thread may include the instructions for a download function within the web browser application.
The local variables of each thread are typically unique. Thus, if two separate instances of a function are executing in two separate threads, each thread may include a distinct set of the local variables associated with that function. On the other hand, static variables and global variables are typically shared by all of the threads in a process.
Function local storage and thread local storage are two mechanisms that may be used to store local variables for a thread. With thread local storage, one can provide unique data for each thread in a process. The variables of a thread that reside in thread local storage may be referred to as “thread local variables” or simply “TLVs.” Under some operating systems, before TLVs can be defined, the process must allocate an index known as a “global index,” and that index must then be used to access any TLVs.
Many multithreaded applications use TLVs widely. For example, when supporting multithreaded Java applications, runtime systems such as Java virtual machines (JVMs) often use TLVs for tasks such as exception handling, garbage collection (GC), and runtime helper routines. Furthermore, a runtime system may access these variables frequently when running Java applications. For instance, when an exception is thrown from a method of the Java application, the JVM needs to perform stack unwinding to the method's previous (caller) frame, in case the thrown exception is not caught or handled by the current method. Unwinding operations are also required for GC, for instance to find the root set of live references during root set enumeration and to fill in the stack trace information for an exception object. One common mechanism in runtime systems for handling exception and stack unwinding is to use TLVs to record the contexts or activation records of the active Java methods of each live thread. TLVs may be used to implement various data structures, including linked lists, for instance.
In conventional systems, one constraint associated with TLVs is that threads must use a kernel system call to access the TLVs. For instance, a kernel system call (e.g., pthread_getspecific) is required to acquire a TLV head pointer in thread packages such as Linux pthread. A disadvantage associated with needing to use kernel system calls to access TLVs is that kernel system calls typically adversely affect the performance of a system, due to trapping into kernel mode or privileged mode. Such traps impose high overhead due to operations such as cache flushes, etc.